#!/usr/bin/perl

use strict;
#no strict "refs";
use warnings;

use Getopt::Long;
use Config::IniFiles;
use Shell qw/touch date/;
use IO::File;
use Data::Dumper;
use Fcntl ':flock';

my $VERSION="2.10";
my $configFile=undef;
my $dataFile=undef;
my $opt = GetOptions (
	"f|config=s" => \$configFile,    # string
	"d|data=s" => \$dataFile,
	);  # flag

unless ( defined ($configFile) and -f $configFile ) {
    print STDERR "Config file doesn't exists or is not defined !\n";
    exit 1;
}

unless ( defined ($dataFile) and -f $dataFile ) {
    print STDERR "Data file doesn't exists or is not defined !\n";
    exit 2;
}

my %ini;
tie %ini, 'Config::IniFiles', ( -file => $configFile );
my $gsep=$ini{'general'}{'separator'};
my $lsep=$ini{'general'}{'lineSeparator'};
my $maxFieldNumber=$ini{'general'}{'maxFieldNumber'};
$lsep="\n" if ($lsep eq "newline");
$lsep="\t" if ($lsep eq "tab");
### LINE SEPARATOR : $lsep
my %fields;
my %filesSep;
my %filesTranslate;
my %filesCheck;
my %filesContent=();

my @usedHandlers=();

my $startCol=$ini{'general'}{'startColunm'};
my $startChar=$ini{'general'}{'startChar'};

foreach my $section ( sort grep /File/, keys %ini ) {
### handling section: $section
    my @files=();
    my @lfields= split/,/, $ini{$section}{'fields'};
    my $name=$ini{'general'}{'outputDirectory'}."/".$ini{$section}{'file'};
    my $zipped=$ini{$section}{'zipped'};
    my $h=openFileHandle ( $name, $zipped );
    for my $id (@lfields) {
	push @{$fields{$id}}, $h;
    }
    $filesSep{$h}=$ini{$section}{'separator'};
    
    my $strTrans=$ini{$section}{'translate'};
    my $htrans= eval "{$strTrans};";
    $filesTranslate{$h}=$htrans;
    if (defined ($ini{$section}{'check'}) and $ini{$section}{'check'} == 1) {
	$filesCheck{$h}{'check'}=1;	
	my $errname=$ini{'general'}{'outputDirectory'}."/".$ini{$section}{'errfile'};
	$filesCheck{$h}{'errfile'}=openFileHandle ($errname, $ini{$section}{'errzipped'});
	$filesCheck{$h}{'numfields'}=$ini{$section}{'numFields'};
    }
}
open( SOURCE, "< $dataFile" )
or die "Couldn't open $dataFile for reading: $!\n";

$startChar='' unless defined $startChar;
$startCol=0 unless defined $startCol;
### startChar: $startChar
### startCol: $startCol


while (<SOURCE>) {
    my @elts=split /$gsep/, trim($_), $maxFieldNumber;

    %filesContent=();
    @usedHandlers=();
    my ($line, $item);
    for (my $i=1; $i<=scalar(@elts);$i++) {
	foreach my $f (@{$fields{$i}}) {
	    $line=$elts[$i-1];
	    foreach my $t ( keys %{$filesTranslate{$f}} ) {
		$item=${$filesTranslate{$f}}{$t};
	    	$item="\\|" if $item eq "\|";	
		$line =~ s/$t/$item/g;
	    }				
	    $line =~ s/\\n/\n/g;
	    $line =~ s/\\t/\t/g;
	    $line =~ s/\\b/\b/g;
	    ### LINE : $line
		### FHANDLER: $f
		$filesContent{$f}{'file'}=$f;
		$filesContent{$f}{'content'}="" unless (defined $filesContent{$f}{'content'} );
		$filesContent{$f}{'content'}=$filesContent{$f}{'content'}.$filesSep{$f} if defined $filesSep{$f} and $filesContent{$f}{'content'} ne "";# and $line ne "";
		$filesContent{$f}{'content'}=$filesContent{$f}{'content'}."$line";
		

	}
    }
### KEYS: keys (%filesContent)
### FILESCONTENT: %filesContent
### FILESEP: %filesSep
### FILECHECK: %filesCheck


    for my $f (keys %filesContent) {
	if ( defined($filesCheck{$f}{'check'}) and $filesCheck{$f}{'check'} == 1 )
	{
	    my @lines=split /\n/, "$filesContent{$f}{'content'}";    
	    my $sep=$filesSep{$f};
	    $sep="\\|" if $sep eq "\|";	
	    

	    for my $subline (@lines) {
		my @items=split /$sep/, "$subline:";
	    	my $count=scalar( @items );
	    	my $expected = $filesCheck{$f}{'numfields'};
	    ### SEParator: $sep
	    ### SPLIT: @items
	    ### CONTENT : $filesContent{$f}{'content'}
	    ### EXCEPTED : $expected
	    ### COUNT : $count
		my $lfh=$filesCheck{$f}{'errfile'};
	   	if ($count == $expected) {
			$lfh=$filesContent{$f}{'file'};
		}
		
		lock $lfh;
		print $lfh $subline;
		print $lfh "\n";
		unlock $lfh;
	    }

	} else {
		### WRITING IN : $f
		my $lfh=$filesContent{$f}{'file'};
		lock $lfh;
		print $lfh $filesContent{$f}{'content'};
		unlock $lfh;
	}
    }
} # END WHILE 

close(SOURCE);

for my $fh (keys %filesSep) {
    close ($filesCheck{$fh}{'errfile'}) if ( defined($filesCheck{$fh}{'check'}) and $filesCheck{$fh}{'check'} == 1 );
    close ($fh);
}

sub trim {
    my $string = shift;
    $string="" unless defined ($string);
    $string =~ s/^\s+//;
    $string =~ s/\s+$//;
    return $string;
}

sub retrieveTrueFileName{
    my $name=shift;
    $name =~ s/(\%.)-(\d+)/date( "--date \"$2 day ago\"", "+$1")/eg;
    $name =~ s/(\%.)/date("+$1")/eg;
    $name =~ s/\n//g;

    return $name;
}

sub openFileHandle {
    my $file= retrieveTrueFileName shift;
    my $zipped=shift;
    my $handle = new IO::File;
    if (defined $zipped and $zipped==1) {
	open ($handle, "| gzip >> $file.gz"); #  we write to a pipe 
    } else {
	touch $file unless ( -f $file );
	die "$file : $@" unless ( -f $file );
	open($handle, ">>$file") or die "$file: $!";
    }
    $handle->autoflush(1);
    ### FILE : $file
    ### HANDLER: $handle
    return $handle;
}

sub lock {
    my $h =shift;
    flock($h,LOCK_EX);
# and, in case someone appended
# while we were waiting...
    seek($h, 0, 2);
}

sub unlock {
 	my $h=shift;
 	flock($h,LOCK_UN);
}
